package cn.changeforyou.web.dao.mybatis.ext.extBuilder.builder;

import cn.changeforyou.web.dao.mybatis.ext.dao.BaseDao;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.DaoSpringHolder;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.SqlContext;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.TableInfoForm;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.writer.SetSqlWriter;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.writer.UpdateSqlWriter;
import cn.changeforyou.web.dao.mybatis.ext.extBuilder.writer.WhereConditionSqlWriter;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * update语句构建器
 *
 * @author zhyu
 * @version 1.0
 * @date 2022/2/10 16:36
 */
public class UpdateBuilder {

    private static UpdateSqlWriter updateSqlWriter;
    private static SetSqlWriter setSqlWriter;
    private static WhereConditionSqlWriter whereConditionSqlWriter;

    static {
        updateSqlWriter = DaoSpringHolder.getBean(UpdateSqlWriter.class);
        setSqlWriter = DaoSpringHolder.getBean(SetSqlWriter.class);
        whereConditionSqlWriter = DaoSpringHolder.getBean(WhereConditionSqlWriter.class);
    }

    /**
     * 构建
     *
     * @param mappedStatementMetaInfo
     * @return
     */
    public static void buildAndSet(MappedStatementMetaInfo mappedStatementMetaInfo) {
        SqlContext sqlContext = prepareBuild(mappedStatementMetaInfo);

        //构建update update xxx
        updateSqlWriter.writeSql(sqlContext);

        //构建set语句
        setSqlWriter.writeSql(sqlContext);

        //构建where 语句
        whereConditionSqlWriter.writeSql(sqlContext);

        String sql = afterBuilder(sqlContext);
        mappedStatementMetaInfo.setSql(sql);
    }

    private static SqlContext prepareBuild(MappedStatementMetaInfo mappedStatementMetaInfo) {
        SqlContext sqlContext = new SqlContext();
        EntityMetaInfo entityMetaInfo = mappedStatementMetaInfo.getEntityMetaInfo();
        sqlContext.setSqlCommandType(mappedStatementMetaInfo.getSqlCommandType());
        sqlContext.setScriptModel(true);
        sqlContext.setMappedStatementResultType(mappedStatementMetaInfo.getMappedStatementBaseInfo().getMappedStatementResultType());
        MappedStatementParameterType mappedStatementParameterType = mappedStatementMetaInfo.getMappedStatementBaseInfo().getMappedStatementParameterType();
        sqlContext.setMappedStatementParameterType(mappedStatementParameterType);

        List<FieldMetaInfo> fieldMetaInfos = null;
        switch (mappedStatementParameterType) {
            case updateDtoIgnoreNull:
                fieldMetaInfos = mappedStatementMetaInfo.getDtoMetaInfo().getFieldMetaInfoList();
                sqlContext.setUpdateIgnoreNull(true);
                break;
            case updateDtoByCondition:
                fieldMetaInfos = mappedStatementMetaInfo.getDtoMetaInfo().getFieldMetaInfoList();
                sqlContext.setUpdateIgnoreNull(true);
                sqlContext.setWhereJavaParamPrefix(BaseDao.CONDITION_PARAM);
                sqlContext.setSetJavaParamPrefix(BaseDao.DTO_PARAM);
                break;
            case updateEntityByCondition:
                fieldMetaInfos = entityMetaInfo.getFieldMetaInfoList();
                sqlContext.setUpdateIgnoreNull(true);
                sqlContext.setSetJavaParamPrefix(BaseDao.ENTITY_PARAM);
                sqlContext.setWhereJavaParamPrefix(BaseDao.CONDITION_PARAM);
                break;
            case updateEntityIgnoreNull:
                fieldMetaInfos = entityMetaInfo.getFieldMetaInfoList();
                sqlContext.setUpdateIgnoreNull(true);
                break;
            case updateDto:
                fieldMetaInfos = mappedStatementMetaInfo.getDtoMetaInfo().getFieldMetaInfoList();
                break;
            case updateEntity:
                fieldMetaInfos = entityMetaInfo.getFieldMetaInfoList();
                break;
        }
        switch (mappedStatementParameterType) {
            case updateEntityIgnoreNull:
            case updateEntity:
            case updateDtoIgnoreNull:
            case updateDto:
                fieldMetaInfos = fieldMetaInfos.stream().filter(x -> !x.isId()).collect(Collectors.toList());
                break;
        }
        sqlContext.setUpdateMetaInfo(fieldMetaInfos);

        TableInfoForm tableInfoForm = new TableInfoForm();
        tableInfoForm.setTableName(entityMetaInfo.getTableName());
        tableInfoForm.setTableAlias(entityMetaInfo.getTableNameAlias());
        sqlContext.setMainTableForm(tableInfoForm);

        //解析where树
        switch (mappedStatementParameterType) {
            case updateEntity:
            case updateEntityIgnoreNull:
            case updateDto:
            case updateDtoIgnoreNull:
                for (FieldMetaInfo fieldMetaInfo : entityMetaInfo.getFieldMetaInfoList()) {
                    if (fieldMetaInfo.isId()) {
                        sqlContext.setPrimaryKeyInfo(fieldMetaInfo);
                        break;
                    }
                }
                List<FieldMetaInfo> primaryKeyField = new ArrayList<>();
                primaryKeyField.add(sqlContext.getPrimaryKeyInfo());
                WhereBuildUtil.buildWhereTreeByFieldMetaInfoList(primaryKeyField, sqlContext);
                break;
            case updateEntityByCondition:
            case updateDtoByCondition:
                ConditionMetaInfo conditionMetaInfo = mappedStatementMetaInfo.getConditionMetaInfo();
                WhereBuildUtil.buildWhereTreeByConditionMetaInfo(conditionMetaInfo, sqlContext);
                break;
        }
        return sqlContext;
    }

    /**
     * build后方法
     *
     * @param context
     * @return
     */
    private static String afterBuilder(SqlContext context) {
        if (context.isScriptModel()) {
            return "<script>" + context.getSql().toString() + "</script>";
        }
        return context.getSql().toString();
    }
}
